#include "Field.h"
#include "Ball.h"
#include <cmath>
#include <QPropertyAnimation>
#include <QDebug>
#include <QGraphicsPixmapItem>

const QSharedPointer<QGraphicsObject> Field::OUT_OF_FIELD = QSharedPointer<QGraphicsObject>();
const QSharedPointer<QGraphicsObject> Field::EMPTY_CELL = QSharedPointer<QGraphicsObject>(new Ball());

Field::Field(QSize size, QObject *parent) : QGraphicsScene(parent), size(size)
{
	setBackgroundBrush(Qt::darkCyan);
	field.resize(size.height());
	for (int i = 0; i < size.height(); i++) {
		field[i].resize(size.width());
		for (int j = 0; j < size.width(); j++) {
			field[i][j] = OUT_OF_FIELD;
		}
	}
}

void Field::addCell(const QPoint &cell)
{
	field[cell.x()][cell.y()] = EMPTY_CELL;
	QGraphicsPixmapItem *cellImage = new QGraphicsPixmapItem(QPixmap(":/icons/cell1.png"));
	addItem(cellImage);
	cellImage->setPos(cell.y() * 50, cell.x() * 50);
	cellImage->setOffset(-33, -33);
	cellImage->setZValue(-1);
}

void Field::addBall(const QPoint& cell, QSharedPointer<QGraphicsObject> ball)
{
	field[cell.x()][cell.y()] = ball;
	ballsNumber++;
	addItem(ball.data());
	ball->setPos(cell.y() * 50, cell.x() * 50);

	connect(ball.data(), SIGNAL(moved(const QPointF&, const QPointF&, Ball*)),
			this, SLOT(ballMoved(QPointF,QPointF,Ball*)));
}

void Field::clearCell(const QPoint &cell)
{
	cellToClear = cell;
	QPropertyAnimation *animation = new QPropertyAnimation(field[cell.x()][cell.y()].data(), "size");
	animation->setDuration(400);
	animation->setEndValue(QSize(1, 1));
	animation->setEasingCurve(QEasingCurve::Linear);
	animation->start(QPropertyAnimation::DeleteWhenStopped);
	connect(animation, SIGNAL(finished()), this, SLOT(removeAnimationFinished()));

	ballsNumber--;
}

void Field::moveBall(const QPoint &from, const QPoint &to)
{
	field[to.x()][to.y()] = field[from.x()][from.y()];
	field[from.x()][from.y()] = EMPTY_CELL;
	field[to.x()][to.y()]->setPos(to.y() * 50, to.x() * 50);
}

QSize Field::getSize() const
{
	return size;
}

bool Field::isEmptyCell(const QPoint& cell) const
{
	return field[cell.x()][cell.y()] == EMPTY_CELL;
}

bool Field::makeMove(const QPoint &from, const QPoint &to)
{
	if (!isPointOnField(from) || !isPointOnField(to) ||
		!isEmptyCell(to) || !isBusyCell(from)) {
		return false;
	}

	QPoint middlePoint;
	if (from.x() == to.x() && abs(from.y() - to.y()) == 2) {
		middlePoint.setX(from.x());
		middlePoint.setY((from.y() + to.y()) / 2);
	} else if (from.y() == to.y() && abs(from.x() - to.x()) == 2) {
		middlePoint.setX((from.x() + to.x()) / 2);
		middlePoint.setY(from.y());
	} else {
		return false;
	}

	if (!isPointOnField(middlePoint) || !isBusyCell(middlePoint)) {
		return false;
	}

	clearCell(middlePoint);
	moveBall(from, to);
	return true;
}

bool Field::isPointOnField(const QPoint& point) const
{
	return point.x() >= 0 && point.x() < size.height() &&
			point.y() >= 0 && point.y() < size.width() &&
			field[point.x()][point.y()] != OUT_OF_FIELD;
}

bool Field::isBusyCell(const QPoint& cell) const
{
	return isPointOnField(cell) && !isEmptyCell(cell);
}

int Field::ballsRemains() const
{
	return ballsNumber;
}

void Field::ballMoved(const QPointF &from, const QPointF &to, Ball *ball)
{
	QPoint f;
	f.setY((int)((from.x() + 25) / 50));
	f.setX((int)((from.y() + 25) / 50));

	QPoint t;
	t.setY((int)((to.x() + 25) / 50));
	t.setX((int)((to.y() + 25) / 50));

	if (makeMove(f, t)) {
		ball->setX(t.y() * 50);
		ball->setY(t.x() * 50);
	} else {
		ball->setX(f.y() * 50);
		ball->setY(f.x() * 50);
	}
}

void Field::removeAnimationFinished()
{
//	removeItem(field[cellToClear.x()][cellToClear.y()].data());
	field[cellToClear.x()][cellToClear.y()] = EMPTY_CELL;
}
